//: [Previous](@previous)

import Foundation

//: ## 枚举
enum Direction1 {
    case north
    case south
    case east
    case west
}

enum Direction2 {
    case north, south, east, west
}
//Direction1与Direction2,写法不一样,作用一样

var dit1 = Direction1.west //新定义一个枚举变量dit1,然后把枚举成员赋值给他; 因为赋值的时候已经确定是枚举Direction1的类型,所以不用在var后面声明系统会自己默认
dit1 = Direction1.east
dit1 = .north
//这是2种赋值方法,因为已经确定是Direction1这个枚举类型,所以可以直接用.** 去赋值
print(dit1) //north

switch dit1 {
case .north:
    print("north")
case Direction1.east:
    print("east")
default:
    print("other")
}

//: ## 关联值(Associated Values)
// 有时会将枚举的成员值 跟 其他类型的关联存储在一起,会非常有用
// 也可以简单理解为,将各种变量的值,直接存到枚举变量里面
enum Score {// 比如有个需求:展示分数,分数有两种,1种是具体分数值,另1种是字母ABC的
    case points(Int) //具体分数值
    case grade(Character) //子母ABC
}
var score1 = Score.points(96) //用变量score1和枚举值的成员point中带96Int的值关联起来,96Int是直接存在score1的内存中
score1 = .grade("A") //改值成字母"A"

switch score1 {
case let .points(j):
    print(j, "points")
case let .grade(j):
    print(j, "grade")
} //A grade
// 枚举先分类型,然后可以用值去接受关联值,就可以使用这个关联值了

//举例: 日期的展示
enum DateAV {
    case digit(year: Int, month: Int, dat: Int) //数字
    case string(String)
}
var date1 = DateAV.digit(year: 2024, month: 4, dat: 22)
date1 = .string("2023-04-05")
switch date1 {
case .digit(let year, let month, let dat):
 // case let .digit(year, month, dat):  这个写法和上一行的一样,把let写在括号()外,代表括号()里面的参数全是let
    print(year, month, dat)
// case .string(let string):  默认是let,但是也可以把括号()里面的let改成var,这样子就可以改值了
case .string(var string):
    string = "22"
    print(string)
}

//举例: 手机的解锁,2种方式,方式1:直接4位数字密码  方式2:9宫格手动划线画图案的
enum PasswordAV {
    case number(Int, Int, Int, Int) // 4位数密码,直接用Int表达好
    case gesture(String) // 手动画图案的,这种没有位数的限制,画多少都行,所以用字符串表达好
}
var psAV = PasswordAV.number(1, 2, 3, 4)
psAV = .gesture("192313294545")
switch psAV {
case .number(let int, let int2, let int3, let int4):
    print(int, int2, int3, int4)
case .gesture(let string):
    print(string)
}
//: ## 原始值(Raw Values)
// 枚举成员可以使用 相同类型 的默认值预先关联,这个默认值叫做:原始值
enum numberRV : Int {
    case a = 10
    case b = 20
}// 在枚举enum命名那里,冒号:后面表达的是次枚举原始值的类型,不是继承
var numRV = numberRV.a
print(numRV) //a
print(numRV.rawValue) //10
print(numberRV.b.rawValue) //20
//等级
enum Grade : String {
    case perfect = "A"
    case great = "B"
    case good = "C"
    case bad = "D"
}
print(Grade.perfect.rawValue) //A
//: ## 隐式原始值
// 如果枚举的原始值是 Int, String 这2个类型,Swift会自动分配原始值
enum Direction3 : String {
    case north = "north"
    case south = "south"
    case east = "east"
    case west = "west"
}
enum Direction4 : String {
    case north, south, east, west
}
// Direction3 等价于 Direction4, 因为Direction4的原始值类型是String,没有写原始值,系统会默认给它赋值上和枚举相同的String作为原始值,这个就是隐式原始值
print(Direction3.south.rawValue) // "south"
print(Direction4.south.rawValue) // "south"

enum Season : Int {
    case spring, summer, autumn, winter
}// Seaon的原始值类型是Int,所以自动赋值0,1,2,3
print(Season.spring.rawValue) //0
print(Season.summer.rawValue) //1

enum Season1 : Int {
    case spring = 1, summer, autumn = 4, winter
}//这种写法也可以自己手动写入原始值,后面的会自动匹配跟上,这里因为spring=1,所以summer自动=2了,但是因为autumn手写成了4,所以winter会自动=5
print(Season1.spring.rawValue) //1
print(Season1.summer.rawValue) //2
print(Season1.autumn.rawValue) //4
print(Season1.winter.rawValue) //5
//: ## 递归枚举
//枚举里面用到这个枚举,就是递归的枚举.必须加上indirect(中文:间接的)
indirect enum AirthExpr {
    case number(Int)
    case sum(AirthExpr, AirthExpr)
    case difference(AirthExpr, AirthExpr)
}//这个需求是一个算数需要,可以用于加和减.枚举类型里面3个类型右边的括号代表关联值
//第二种写法,
enum AirthExpr1 {
    case number(Int)
    indirect case sum(AirthExpr1, AirthExpr1)
    indirect case difference(AirthExpr1, AirthExpr1)
}// 把indirect放到里面写
let five = AirthExpr.number(5)
let four = AirthExpr.number(4)
let two = AirthExpr.number(2)
let sum = AirthExpr.sum(two, four) //因为 sum(AirthExpr, AirthExpr) 的关联值是AirthExpr枚举类型,two就是,所以可以直接把two放到参数里面
let difference = AirthExpr.difference(five, two)

//传入一个枚举AirthExpr,得到结果number
func caculate(_ expr: AirthExpr) -> Int {
    switch expr {
    case .number(let int):
        return int
    case .sum(let airthExpr, let airthExpr2):
        var leftInt: Int = 0
        var rightInt: Int = 0
        
        switch airthExpr {
        case .number(let int):
            leftInt = int
        default: 
            break
        }
        
        switch airthExpr2 {
        case .number(let int):
            rightInt = int
        default:
            break
        }
        
        return leftInt + rightInt
//    case .difference(let airthExpr, let airthExpr2):
//        <#code#> 这里只写了加法,减法懒得写了,感觉这个代码有点low,以后有能力了再优化
    default : return 0
    }
}
caculate(sum) //AirthExpr.number(2) + AirthExpr.number(4) = 6
//: ## MemoryLayout
//查看内存大小
MemoryLayout<Int>.size // 实际用到的空间大小
MemoryLayout<Int>.stride // 实际占用的空间大小
MemoryLayout<Int>.alignment //对齐参数,内存对齐,就是假如现在只占6个字节,但是这个64位的系统,最小字节是8位,所以此时是8位
//举例1
enum PasswordML {
    case number(Int, Int, Int, Int)
    case other
}
var pwd = PasswordML.number(8, 5, 2, 4)
pwd = .other
//问题?:请问pwd内存的在186行和187行 分别多少个字节
//回答:枚举的关联值,是直接存在变量里面的,所以pwd里面真实存了4个Int,一个Int是8位,所以pwd在186行是32位字节.然后因为在186行的时候pwd已经是32位字节了,内存大小不会动态改变,哪怕没用也会保留,所以在187行的时候pwd还是32位
MemoryLayout<PasswordML>.size //33 (32个字节应该是存.number; 1个字节应该是存.other)
MemoryLayout<PasswordML>.stride //40
MemoryLayout<PasswordML>.alignment //8 ,所以size必须是8的倍数
//举例2
enum SeasonML {
    case spring, summer, autumn, winter
}
var s = SeasonML.summer
MemoryLayout<SeasonML>.size //1
MemoryLayout<SeasonML>.stride //1
MemoryLayout<SeasonML>.alignment //1
//因为这个SeasonML枚举特别简单,所以1个字节就行了,一个字节8位,足够存了.内存只需要存个0 1 2 3(这些数字是一些标记,不是指Int)就可以很好的表达spring, summer, autumn, winter了 
/** 关联值和原始值的区别
        关联值:关联值是枚举变量可以存进多次不同的值的,比如var pwd = PasswordML.number(8, 5, 2, 4);var pwd = PasswordML.number(888, 555, 2222, 4444),所以关联值需要有单独的固定的内存去存储这些具体的值,所以这个枚举变量的内存大小和关联值的具体的值的大小是直接有关的
 
        原始值:原始值是固定死的,和成员永远绑定死,比如case spring = 1, summer, autumn = 4, winter,永远不变的不允许改变
*/














//: [Next](@next)
